"""
@Author: li
@Email: lijianqiao2906@live.com
@FileName: file_service.py
@DateTime: 2025-06-18
@Docs: 文件导入导出服务
"""

import io

import polars as pl
from fastapi import HTTPException, UploadFile, status
from tortoise import transactions

from app.schemas.file import FileImportResponse
from app.services import (  # type: ignore
    AreaService,
    BrandService,
    DeviceGroupService,
    DeviceModelService,
    DeviceService,
)
from app.utils import LogConfig, logger, system_log


class FileService:
    """文件服务，处理设备信息的导入和导出"""

    def __init__(self):
        self.device_service = DeviceService()
        self.brand_service = BrandService()
        self.device_model_service = DeviceModelService()
        self.area_service = AreaService()
        self.device_group_service = DeviceGroupService()

    @system_log(
        LogConfig(
            operation_type="CREATE",
            resource_type="DEVICE",
            log_args=True,
            log_result=True,
            mask_sensitive=["password", "account"],
        )
    )
    async def import_devices(self, file: UploadFile, user: str = "system") -> FileImportResponse:
        """从CSV或Excel文件导入设备信息"""
        await self._validate_file(file)

        try:
            content = await file.read()
            if not file.filename:
                raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="上传文件没有名称")
            df = self._read_file_to_dataframe(content, file.filename)
            self._validate_headers(df.columns)

            total_rows = len(df)
            success_count = 0
            skipped_count = 0

            async with transactions.in_transaction():
                for i, row in enumerate(df.to_dicts()):
                    row_num = i + 2  # 考虑到表头，行号从2开始
                    try:
                        # create_device_from_import 现在可能返回 None
                        created_device = await self.device_service.create_device_from_import(row)
                        if created_device:
                            success_count += 1
                        else:
                            skipped_count += 1
                    except Exception as e:
                        logger.error(f"导入文件第 {row_num} 行失败: {e}")
                        # 遇到任何错误，整个事务回滚
                        raise HTTPException(
                            status_code=status.HTTP_400_BAD_REQUEST,
                            detail=f"第 {row_num} 行处理失败: {e}",
                        ) from e

            return FileImportResponse(
                total_rows=total_rows,
                success_rows=success_count,
                skipped_rows=skipped_count,
                message=f"设备信息导入完成。成功 {success_count} 条，跳过 {skipped_count} 条。",
            )

        except HTTPException as e:
            raise e
        except Exception as e:
            logger.error(f"文件处理失败: {e}")
            raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"文件处理失败: {e}") from e

    @system_log(
        LogConfig(
            operation_type="READ",
            resource_type="DEVICE",
            log_result=True,
        )
    )
    async def export_devices(self, user: str = "system") -> tuple[io.BytesIO, str, str]:
        """导出设备信息到Excel文件"""
        devices = await self.device_service.get_all_devices_for_export()
        if not devices:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="没有可导出的设备")

        df = pl.DataFrame(devices)
        buffer = io.BytesIO()

        # xlsx格式导出
        df.write_excel(buffer)
        media_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
        filename = "devices.xlsx"

        buffer.seek(0)
        return buffer, media_type, filename

    def _read_file_to_dataframe(self, content: bytes, filename: str) -> pl.DataFrame:
        """将文件内容读取到Polars DataFrame"""
        buffer = io.BytesIO(content)
        if filename.endswith(".csv"):
            return pl.read_csv(buffer)
        elif filename.endswith(".xlsx"):
            return pl.read_excel(buffer)
        else:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="不支持的文件格式")

    def _validate_headers(self, columns: list[str]):
        """验证文件头是否符合规范"""
        required_headers = {
            "name",
            "management_ip",
            "port",
            "account",
            "password",
            "brand_name",
            "model_name",
            "device_type",
            "area_name",
        }
        if not required_headers.issubset(set(columns)):
            missing = required_headers - set(columns)
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"文件缺少必要的列: {', '.join(missing)}",
            )

    async def _validate_file(self, file: UploadFile):
        """验证上传的文件"""
        if not file.filename:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="上传文件没有名称")

        if not (file.filename.endswith(".csv") or file.filename.endswith(".xlsx")):
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="文件格式无效，请上传 CSV 或 XLSX 文件",
            )
